package com.tcs.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;

import com.tcs.tsrm.beans.ClusteringThresholds;

public class RepeatableClusters {

	private static double mDistance = 0;
	private static double minSupport = 0;

	public void findNonRepeatingClusters(String inputFolderPath,
			String outputFolderPath, ClusteringThresholds thresholds)
					throws IOException {
		mDistance = thresholds.getMotifWidth();
		minSupport = thresholds.getSupport();
		HashMap<String, ArrayList<Integer>> allClusters = loadClusters(inputFolderPath);
		long shitedClusterRemovalStart = System.currentTimeMillis();
		ArrayList<String> shiftedClusters = remRepeatableClusters(allClusters);
		long shitedClusterRemovalStop = System.currentTimeMillis();
		long shiftedClusterRemovalTime = shitedClusterRemovalStop
				- shitedClusterRemovalStart;
		System.out.println("Shifted Cluster Removal Time->"
				+ (double) shiftedClusterRemovalTime / 1000 + " seconds");
		printNonRepeatableClusters(allClusters, shiftedClusters,
				outputFolderPath);
		printRejectedClusters(shiftedClusters, outputFolderPath,thresholds);

		System.out.println("Shifted Clusters Removal Done...");
	}

	private static void printRejectedClusters(
			ArrayList<String> shiftedClusters, String outputFolderPath,ClusteringThresholds thresholds)
					throws IOException {
		if(thresholds.isDebug()==true)
		{
			File fileOriginal = new File(outputFolderPath
					+ "-Rejected-Cluster-Indexes.txt");
			PrintWriter statOut2 = new PrintWriter(new BufferedWriter(
					new FileWriter(fileOriginal, false)));
			for (String clusterId : shiftedClusters) {
				statOut2.write(clusterId + "\n");
			}
			statOut2.close();
		}
	}

	private static ArrayList<String> remRepeatableClusters(
			HashMap<String, ArrayList<Integer>> allClusters) {
		// System.out.println("original");
		Set<String> clusterIndexSet = allClusters.keySet();
		int count = 0;
		ArrayList<String> shiftedClusters = new ArrayList<String>();
		ArrayList<String> visitedClusters = new ArrayList<String>();
		for (String clusterId1 : clusterIndexSet) {
			visitedClusters.add(clusterId1);
			if (shiftedClusters.contains(clusterId1)) {
				continue;
			}
			for (String clusterId2 : clusterIndexSet) {
				String cId1 = "";
				ArrayList<Integer> cluster1 = new ArrayList<Integer>();
				ArrayList<Integer> cluster2 = new ArrayList<Integer>();
				if (shiftedClusters.contains(clusterId2)
						|| clusterId1 == clusterId2
						|| visitedClusters.contains(clusterId2)) {
					continue;
				}
				if (allClusters.get(clusterId1).size() < allClusters.get(
						clusterId2).size()) {
					cId1 = clusterId1;
					cluster1 = allClusters.get(clusterId1);
					cluster2 = allClusters.get(clusterId2);

				} else {
					cId1 = clusterId2;
					cluster1 = allClusters.get(clusterId2);
					cluster2 = allClusters.get(clusterId1);
				}
				count++;
				boolean repeat = findRepeatableCluster(cluster1, cluster2,
						count);
				if (repeat) {
					shiftedClusters.add(cId1);
					if (cId1 == clusterId1)
						break;
					continue;
				}
			}
		}
		return shiftedClusters;

	}

	private static boolean findRepeatableCluster(ArrayList<Integer> cluster1,
			ArrayList<Integer> cluster2, int count) {
		ArrayList<Integer> allDistances = new ArrayList<Integer>();
		int distance = 0;
		int prevDist = 0;
		boolean flag = false;
		int tracker = 0;
		for (int i = 0; i < cluster1.size(); i++) {
			int minDist = Integer.MAX_VALUE;
			prevDist = Integer.MAX_VALUE;
			for (int j = tracker; j < cluster2.size(); j++) {
				distance = cluster2.get(j) - cluster1.get(i);
				if (distance > mDistance && flag == false) {
					flag = false;
					break;
				} else if (distance < 0 || flag == true) {
					flag = true;
					if (Math.abs(distance) > prevDist) {
						if (minDist <= mDistance) {

							allDistances.add(minDist);
						}
						flag = false;
						break;
					}
					if (Math.abs(distance) < minDist) {
						minDist = Math.abs(distance);
						tracker = j + 1;
					}
					prevDist = Math.abs(distance);
					continue;
				} else {
					flag = false;
					allDistances.add(distance);
					tracker = j + 1;
					break;
				}
			}
		}
		if (allDistances.size() != 0) {

			if ((double) cluster1.size() / allDistances.size() <= 2) {
				double sD = calculateSD(allDistances);
				if (sD < 2)
					return true;
				else
					return false;
			}
		}
		return false;
	}

	private static double calculateSD(ArrayList<Integer> allDistances) {

		double mean = calculateMean(allDistances);
		double variance = calculateVariance(allDistances, mean);
		return (Math.sqrt(variance));
	}

	private static double calculateVariance(ArrayList<Integer> allDistances,
			double mean) {
		double deviations = 0;
		for (Integer d : allDistances) {
			deviations += Math.pow((d - mean), 2);
		}
		return deviations / ((double) allDistances.size());
	}

	private static double calculateMean(ArrayList<Integer> allDistances) {

		int sum = 0;
		for (Integer d1 : allDistances) {
			sum += d1;
		}
		return (double) sum / allDistances.size();
	}

	private static void printNonRepeatableClusters(
			HashMap<String, ArrayList<Integer>> allClusters,
			ArrayList<String> shiftedClusters, String outputFolderPath)
					throws IOException {
		File fileOriginal = new File(outputFolderPath
				+ "-Non-Repeatable-Clusters.txt");
		PrintWriter statOut2 = new PrintWriter(new BufferedWriter(
				new FileWriter(fileOriginal, false)));
		Set<String> clusterIndexSet = allClusters.keySet();
		for (String clusterId : clusterIndexSet) {
			if (shiftedClusters.contains(clusterId)) {
				continue;
			}
			String pointString = getPointString(allClusters.get(clusterId));
			statOut2.write(clusterId + pointString + "\n");
		}
		statOut2.close();

	}

	private static String getPointString(ArrayList<Integer> allPoints) {
		String pointString = "";
		for (Integer point : allPoints) {
			pointString = pointString + "," + point;
		}
		return pointString;
	}

	private static HashMap<String, ArrayList<Integer>> loadClusters(
			String inputFolderPath) {

		FileInputStream fstream;
		HashMap<String, ArrayList<Integer>> allClusters = new HashMap<String, ArrayList<Integer>>();
		try {
			fstream = new FileInputStream(inputFolderPath);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String[] pointIndexes = null;
			String strLine;
			while ((strLine = br.readLine()) != null) {
				pointIndexes = strLine.split(",");
				if (pointIndexes.length > minSupport) {
					ArrayList<Integer> points = sortPoints(pointIndexes);
					allClusters.put(pointIndexes[0], points);
				}
			}
			fstream.close();
		} catch (Exception e) {

			e.printStackTrace();
		}

		return allClusters;
	}

	private static ArrayList<Integer> sortPoints(String[] pointIndexes) {
		ArrayList<Integer> sortedPoints = new ArrayList<Integer>();
		for (int i = 1; i < pointIndexes.length; i++) {
			sortedPoints.add(Integer.valueOf((pointIndexes[i])));
		}
		Collections.sort(sortedPoints);
		return sortedPoints;
	}

}
